package org.dingfugui.triple.protocol.binary.aio;

import java.nio.ByteBuffer;

/**
 * 请求报文格式
 * 报文头(4):报文长度(3)+类型(1)
 * 报文体(n)
 * Created by fugui.ding on 2015/4/20.
 */
public class AioDataReader {

    private AioProducerConnection connection;
    private int bufferReadOffset = 0;

    public AioDataReader(AioProducerConnection connection) {
        this.connection = connection;
    }

    public void onData(ByteBuffer bb, Integer result) {

        int position = bb.position();
        int offset = bufferReadOffset;

        while (true) {//一次可能读到多条报文，所以循环处理
            int length = getLength(bb, offset);

            if (length == -1) {
                //读不到可能是因为缓冲区已满
                if (!bb.hasRemaining()) {
                    //扩容
                    bb = fat(bb);
                }
                break;//继续读
            }

            if (length <= position - offset) {//如果要读取的下一条报文小于当前可读的长度

                byte[] data = new byte[length];
                bb.position(offset);//坐标移动到开始位置
                bb.get(data, 0, length);

                handle(data);

                offset += length;//增加以读取坐标

                //判断缓冲区剩余空间，如果足够则继续读取，如果不足，则清理以读取数据
                if (bb.position() == offset) {
                    //本次读取后缓冲区正好没有下一条报文，则可以清理
                    bb.clear();
                    bufferReadOffset = 0;
                    break;
                } else {
                    bufferReadOffset = offset;
                    bb.position(position);
                    continue;
                }
            } else {
                //如果不够,可能是尚未读到，则继续读取，也可能是ByteBuffer已满，需要扩容
                if (!bb.hasRemaining()) {
                    //扩容
                    bb = fat(bb);
                }
                break;
            }
        }
    }

    private ByteBuffer fat(ByteBuffer bb) {
        int size = bb.capacity();
        size = size << 1;//左移一位，加倍
        ByteBuffer newBb = ByteBuffer.allocate(size);
        newBb.put(bb);
        return newBb;
    }

    private void handle(byte[] data) {
        connection.handle(data);
    }

    //获取下一条报文长度
    private int getLength(ByteBuffer bb, int offset) {
        //int headSize = handler.getMessageHeadSize();//报文头长度
        int headSize = 4;

        if (bb.position() - offset < headSize) {//当前可读少于报文头
            return -1;
        }

        byte b1 = bb.get(offset);
        byte b2 = bb.get(offset + 1);
        byte b3 = bb.get(offset + 2);

        int length = (b1 & 0xFF) | ((b2 & 0xFF) << 8) | ((b3 & 0xFF) << 16);
        return length;
    }
}
